goog.provide('lfpug.ui.ComponentFactory');
goog.provide('lfpug.ui.ComponentFactory.FactoryClass');
goog.provide('lfpug.ui.ComponentFactory.FactoryComponent');
goog.provide('lfpug.ui.ComponentFactory.FactoryContext');
goog.provide('lfpug.ui.ComponentFactory.FactoryMethod');
goog.provide('lfpug.ui.ComponentFactory.FactoryModel');

goog.require('lfpug.Factory');



/**
 * @constructor
 * @extends {lfpug.Factory}
 */
lfpug.ui.ComponentFactory = function() {
  goog.base(this);
};
goog.inherits(lfpug.ui.ComponentFactory, lfpug.Factory);


/**
 * @typedef {Function}
 */
lfpug.ui.ComponentFactory.FactoryClass;


/**
 * @typedef {string}
 */
lfpug.ui.ComponentFactory.FactoryContext;


/**
 * @typedef {goog.ui.Component}
 */
lfpug.ui.ComponentFactory.FactoryComponent;


/**
 * @typedef {Object}
 */
lfpug.ui.ComponentFactory.FactoryModel;


/**
 * @typedef {function(
 *            lfpug.ui.ComponentFactory.FactoryModel) :
 *            lfpug.ui.ComponentFactory.FactoryComponent }
 */
lfpug.ui.ComponentFactory.FactoryMethod;


/**
 * @param {lfpug.ui.ComponentFactory.FactoryClass} factory_class
 *        The constructor class for the model.
 *
 * @param {lfpug.ui.ComponentFactory.FactoryMethod} factory_method
 *        Method to create a new component.
 *
 * @param {lfpug.ui.ComponentFactory.FactoryContext=} opt_context
 *        The context.
 */
lfpug.ui.ComponentFactory.prototype.register =
    function(factory_class, factory_method, opt_context) {

  if (!goog.isFunction(factory_class)) {
    throw new Error('IS not a factory');
  }

  goog.base(this, 'register', function(component, context) {
    return (component instanceof factory_class) &&
        (!goog.isDef(opt_context) || context === opt_context);
  }, factory_method);
};


/**
 * @param {lfpug.ui.ComponentFactory.FactoryModel} model
 *        The model to create a component from.
 *
 * @param {lfpug.ui.ComponentFactory.FactoryContext=} opt_context
 *        Optional, context to limit factories to.
 *
 * @return {lfpug.ui.ComponentFactory.FactoryComponent}
 *         The resulting component.
 */
lfpug.ui.ComponentFactory.prototype.create;


/**
 * @private
 * @type {number}
 */
lfpug.ui.ComponentFactory.count_ = 0;


/**
 * @param {string=} opt_message
 *        The message.
 * @return {lfpug.ui.ComponentFactory.FactoryContext}
 *         A unique context.
 */
lfpug.ui.ComponentFactory.getUniqueContext = function(opt_message) {
  var context = String(lfpug.ui.ComponentFactory.count_++);
  if (!COMPILED || goog.DEBUG) {
    context += '_' + opt_message;
  }
  return  /** @type {string} */ (context);
};


/**
 * @private
 * @type {lfpug.ui.ComponentFactory}
 */
lfpug.ui.ComponentFactory.global_factory_;


/**
 *  @return {lfpug.ui.ComponentFactory}
 *          The component factory.
 */
lfpug.ui.ComponentFactory.getGlobalFactory = function() {
  if (!goog.isDef(lfpug.ui.ComponentFactory.global_factory_)) {
    lfpug.ui.ComponentFactory.global_factory_ =
        new lfpug.ui.ComponentFactory();
  }
  return lfpug.ui.ComponentFactory.global_factory_;
};


/**
 * @param {lfpug.ui.ComponentFactory.FactoryModel} model
 *        The model to create a component from.
 *
 * @param {lfpug.ui.ComponentFactory.FactoryContext=} opt_context
 *        Optional, context to limit factories to.
 *
 * @return {lfpug.ui.ComponentFactory.FactoryComponent}
 *         The resulting component.
 */
lfpug.ui.ComponentFactory.create = function(model, opt_context) {
  var factory = lfpug.ui.ComponentFactory.getGlobalFactory();
  return factory.create.apply(factory, arguments);
};


/**
 * @param {lfpug.ui.ComponentFactory.FactoryClass} factory_class
 *        The constructor class for the model.
 *
 * @param {lfpug.ui.ComponentFactory.FactoryMethod} factory_method
 *        Method to create a new component.
 *
 * @param {lfpug.ui.ComponentFactory.FactoryContext=} opt_context
 *        The context.
 */
lfpug.ui.ComponentFactory.register =
    function(factory_class, factory_method, opt_context) {
  var factory = lfpug.ui.ComponentFactory.getGlobalFactory();
  factory.register.apply(factory, arguments);
};



